/** RPI
    This is a singleton class , which has been created to access global functions and variables for wimax.
    There is a global beta table and BLER table.

    RPI**/

#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <math.h>
#include "globalparams_wimax.h"

using namespace std;



namespace
{
GlobalParams_Wimax *instance = 0;	//Address of the singleton
}


GlobalParams_Wimax::GlobalParams_Wimax ()
{
    Num_SINR_Decimals = 1;
    int FileOK;
    FileOK = ReadTableFromFile ();
    LoadBetaFile ();

}

GlobalParams_Wimax::~GlobalParams_Wimax ()
{

    delete instance;
    instance = 0;

    //release memory
    free (Table_Index);
    free (Table_SINR);
    free (Table_BLER);

}

//The "official" access point

GlobalParams_Wimax *
GlobalParams_Wimax::Instance ()
{
    //"Lazy" initialization. Singleton not created until it's needed

    if (!instance)
    {
        instance = new GlobalParams_Wimax;
    }

    return instance;
}


int
GlobalParams_Wimax::CountLines (std::ifstream & In)
{
    return static_cast < int >(count (std::istreambuf_iterator < char >(In),
                                      std::istreambuf_iterator < char >(),
                                      '\n'));
}

double
GlobalParams_Wimax::GetTablePrecision (double FullDoubleNumber)
{
    int TruncatedInt;

    double TruncatedDouble;

    FullDoubleNumber = FullDoubleNumber * pow (10, Num_SINR_Decimals);
    TruncatedInt = (int) FullDoubleNumber;
    TruncatedDouble = (double) TruncatedInt;

    TruncatedDouble = TruncatedDouble / (pow (10, Num_SINR_Decimals));

    return TruncatedDouble;

}

double
GlobalParams_Wimax::TableLookup (int Index, double SINR)
{
    //in the table provided, SINR is given in increments of 0.1 (ie. 1.1, 1.2, 1.3, etc.)
    //we lookup(Index, truncate(SINR)) and the value above it in the table (Index, truncate(SINR + 0.1))
    //we linearly interpolate the BLER to the actual SINR provided

    double BLER_low = double (NULL);
    double BLER_high = double (NULL);	//when BLER_low is found, BLER_high is taken to be the next BLER in the table
    double SINR_low = GetTablePrecision (SINR);
    double SINR_high = SINR_low + pow (10, -Num_SINR_Decimals);	//SINR_high is one "table precision unit" above SINR_low

    cout << "SINR_low = " << SINR_low << endl;
    cout << "SINR_high = " << SINR_high << endl;

    cout << "SINR: " << SINR << endl;
    cout << "Low: " << Table_Low_SINR_Bounds[Index] << endl;
    cout << "High: " << Table_High_SINR_Bounds[Index] << endl;

    if (SINR < Table_Low_SINR_Bounds[Index])
        return 1;			// we are outside the range (lower limit) of the table, so the packet is definilty in error

    if (SINR > Table_High_SINR_Bounds[Index])
        return 0;			//we are outside the rang of the table (upper limit), so the packet is definitly received correctly

    //Check if we have an exact match.  If so, simply return the value from the SINR lookup.
    for (int i = 0; i < NumLines - 1; i++)
    {
        if ((Table_Index[i] == Index) && (Table_SINR[i] == SINR))
        {
            cout << "Exact match (in TableLookup() ): " << Table_BLER[i] <<
                 endl;
            return Table_BLER[i];
            break;
        }
    }

    //if we get to here, there has not been an exact match
    //cout << endl << "No exact match... getting interpolated match..." << endl;

    //get bounding BLERs
    for (int i = 0; i < NumLines - 1; i++)
    {
        //cout << Table_Index[i] << " " << Index << endl;
        //cout << Table_SINR[i] << " " << SINR_low << endl;
        if ((Table_Index[i] == Index) && (Table_SINR[i] == SINR_low))
        {
            //cout << "BLER_low found to be " << Table_BLER[i] << endl;
            BLER_low = Table_BLER[i];
            BLER_high = Table_BLER[i + 1];
            cout << "BLER_low found to be " << Table_BLER[i] << " high = " <<
                 Table_BLER[i + 1] << endl;
        }
    }

    double Percent_between_SINR;
    //check if there is a partial match
    if (BLER_low != double (NULL))
    {
        //interpolate BLER values
        Percent_between_SINR = (SINR - SINR_low) / (SINR_high - SINR_low);	//percent between SINR

        double TableLookupBLER =
            (BLER_low + (Percent_between_SINR * (BLER_high - BLER_low)));
        cout << "BLER after interpolation found to be " << TableLookupBLER <<
             endl;

        return TableLookupBLER;
    }
    cout << "i am here\n";
    //if all else fails
    cout << "No Match Found!";
    cout << "i am here\n";
    return (double) NULL;


}

int
GlobalParams_Wimax::ReadTableFromFile ()
{

    cout << "Enter ReadTableFromFile()" << endl;

    int NumIndex = 32;
    //open file to get numLines
    ifstream LineFile;
    LineFile.open ("BLER_LookupTable.txt");

    if (LineFile.fail ())
    {
        cerr << "Error opening file BLER_LookupTable.txt\n";
        exit (0);
    }

    NumLines = CountLines (LineFile);
    //cout << "num lines = " << NumLines << endl;

    LineFile.close ();		//close the file

    //OPEN TABLE FILE
    ifstream TableFile;
    TableFile.open ("BLER_LookupTable.txt");

    Table_Index = (int *) malloc (NumLines * sizeof (int));
    //check if malloc worked
    if (Table_Index == NULL)
    {
        fprintf (stderr, "Error: Table_Index is NULL\n");
        free (Table_Index);
        exit (1);
    }

    Table_SINR = (double *) malloc (NumLines * sizeof (double));
    //check if malloc worked
    if (Table_SINR == NULL)
    {
        fprintf (stderr, "Error: Table_SINR is NULL\n");
        free (Table_SINR);
        exit (1);
    }

    Table_BLER = (double *) malloc (NumLines * sizeof (double));
    //check if malloc worked
    if (Table_BLER == NULL)
    {
        fprintf (stderr, "Error: Table_BLER is NULL\n");
        free (Table_BLER);
        exit (1);
    }

    Table_Low_SINR_Bounds = (double *) malloc ((NumIndex + 1) * sizeof (double));	//so array indexes match table indexes
    //check if malloc worked
    if (Table_Low_SINR_Bounds == NULL)
    {
        fprintf (stderr, "Error: Table_Low_SINR_Bounds is NULL\n");
        free (Table_Low_SINR_Bounds);
        exit (1);
    }

    Table_High_SINR_Bounds = (double *) malloc ((NumIndex + 1) * sizeof (double));	//so array indexes match table indexes
    //check if malloc worked
    if (Table_High_SINR_Bounds == NULL)
    {
        fprintf (stderr, "Error: Table_High_SINR_Bounds is NULL\n");
        free (Table_High_SINR_Bounds);
        exit (1);
    }

    //cout << "Before input loop" << endl;

    int CurrentLine = 0;
    while (!TableFile.eof ())
    {
        TableFile >> Table_Index[CurrentLine];
        //cout << "Index: " << Table_Index[CurrentLine] << endl;
        TableFile >> Table_SINR[CurrentLine];
        //cout << "SINR: " << Table_SINR[CurrentLine] << endl;
        TableFile >> Table_BLER[CurrentLine];
        //cout << "BLER: " << Table_BLER[CurrentLine] << endl;

        CurrentLine++;
    }

    TableFile.close ();		// close the file

    //cout << "after read file" << endl;

    Table_Low_SINR_Bounds[0] = double (NULL);	//not used
    Table_High_SINR_Bounds[0] = double (NULL);	//not used

    Table_Low_SINR_Bounds[1] = Table_SINR[0];	//first line in file
    //    cout << "LOW 1 = " << Table_Low_SINR_Bounds[1] << endl;

    Table_High_SINR_Bounds[NumIndex] = Table_SINR[NumLines - 1];	//last line in file
    //    cout << "HIGH 32 = " << Table_High_SINR_Bounds[NumIndex] << endl;

    int BreaksFound = 0;
    //cout << "Before break finding loop..." << endl;

    for (int counter = 1; counter <= NumLines; counter++)
    {
        int TempIndex1, TempIndex2;
        TempIndex1 = Table_Index[counter];
        TempIndex2 = Table_Index[counter + 1];
        if (TempIndex1 != TempIndex2)
        {
            Table_High_SINR_Bounds[BreaksFound + 1] = Table_SINR[counter];
            Table_Low_SINR_Bounds[BreaksFound + 2] = Table_SINR[counter + 1];

            BreaksFound++;
            //                    cout << "Breaks Found = " << BreaksFound << endl;

            //                    cout << "HIGH " << (BreaksFound) << " = " << Table_High_SINR_Bounds[BreaksFound] << endl;
            //                    cout << "LOW " << (BreaksFound + 1) << " = " << Table_Low_SINR_Bounds[BreaksFound + 1] << endl;
            //                    cout << endl;
        }

        if (BreaksFound == (NumIndex - 1))
            break;
    }

    //if we get to here, all is well
    return 1;
}


int
GlobalParams_Wimax::LoadBetaFile ()
{

    ifstream BetaFile;
    BetaFile.open ("BetaTable.txt");

    if (BetaFile.fail ())
    {
        cerr << "Error opening file BetaTable.txt\n";
        exit (0);
    }

    /*Model Index
       PedB=1
       VehA=2
     */

    int Model, Index;
    double TempBeta;

    while (!BetaFile.eof ())
    {
        BetaFile >> Model;
        //cout << "Model " << Model << endl;
        BetaFile >> Index;
        //cout << "Index " << Index << endl;
        BetaFile >> TempBeta;
        //cout << "Beta " << TempBeta << endl;

        Beta[Model][Index] = TempBeta;
    }

    //cout << endl << Beta[1][16] << endl;

    return 0;
}
